﻿using System;
using System.Threading;

namespace Loki
{
    public static class ThreadingExtensions
    {
        public static TResult WithWrite<TResult>(this ReaderWriterLockSlim P_Lock, Func<TResult> P_Functor)
        {
            P_Lock.EnterWriteLock();
            try
            {
                return P_Functor();
            }
            finally
            {
                P_Lock.ExitWriteLock();
            }
        }

        public static void WithWrite(this ReaderWriterLockSlim P_Lock, Action P_Functor)
        {
            P_Lock.EnterWriteLock();
            try
            {
                P_Functor();
            }
            finally
            {
                P_Lock.ExitWriteLock();
            }
        }

        public static TResult WithRead<TResult>(this ReaderWriterLockSlim P_Lock, Func<TResult> P_Functor)
        {
            P_Lock.EnterReadLock();
            try
            {
                return P_Functor();
            }
            finally
            {
                P_Lock.ExitReadLock();
            }
        }

        public static void WithRead(this ReaderWriterLockSlim P_Lock, Action P_Functor)
        {
            P_Lock.EnterReadLock();
            try
            {
                P_Functor();
            }
            finally
            {
                P_Lock.ExitReadLock();
            }
        }

        public static void WithReadWrite(this ReaderWriterLockSlim P_Lock, Func<bool> P_Discriminator, Action P_Read, Action P_Write)
        {
            bool L_NeedWrite = false;
            P_Lock.EnterUpgradeableReadLock();
            try
            {
                L_NeedWrite = P_Discriminator();
            }
            catch
            {
                P_Lock.ExitUpgradeableReadLock();
                throw;
            }

            if (L_NeedWrite)
            {
                // write
                P_Lock.EnterWriteLock();
                try
                {
                    P_Write();
                }
                finally
                {
                    P_Lock.ExitWriteLock();
                    P_Lock.ExitUpgradeableReadLock();
                }
            }
            else
            {
                // read
                P_Lock.EnterReadLock();
                P_Lock.ExitUpgradeableReadLock();
                try
                {
                    P_Read();
                }
                finally
                {
                    P_Lock.ExitReadLock();
                }
            }
        }

        public static TResult WithReadWrite<TResult>(this ReaderWriterLockSlim P_Lock, Func<bool> P_Discriminator, Func<TResult> P_Write, Func<TResult> P_Read)
        {
            bool L_NeedWrite = false;
            P_Lock.EnterUpgradeableReadLock();
            try
            {
                L_NeedWrite = P_Discriminator();
            }
            catch
            {
                P_Lock.ExitUpgradeableReadLock();
                throw;
            }

            if (L_NeedWrite)
            {
                // write
                P_Lock.EnterWriteLock();
                try
                {
                    return P_Write();
                }
                finally
                {
                    P_Lock.ExitWriteLock();
                    P_Lock.ExitUpgradeableReadLock();
                }
            }
            else
            {
                // read
                P_Lock.EnterReadLock();
                P_Lock.ExitUpgradeableReadLock();
                try
                {
                    return P_Read();
                }
                finally
                {
                    P_Lock.ExitReadLock();
                }
            }
        }
    }
}
